/**
 * @file mask_operations.cpp
 * @author 逆流 (1171267147@qq.com)
 * @brief 用掩码矩阵对图像进行卷积计算
 * @version 0.1
 * @date 2024-07-03
 *
 * @copyright Copyright (c) 2024
 * https://docs.opencv.org/4.x/d7/d37/tutorial_mat_mask_operations.html
 */

#include <iostream>
#include <opencv2/opencv.hpp>
#include <string>

#include "env.h"
#include "my_tools.h"

using namespace std;
using namespace cv;

const char kKernel[3][3] = {
    {0,  -1, 0 },
    {-1, 5,  -1},
    {0,  -1, 0 }
};

void Sharpen(const Mat& input, Mat& result) {
    CV_Assert(input.depth() == CV_8UC1);

    const int kChannels = input.channels();
    result.create(input.size(), input.type());

    for (int row = 1; row < input.rows - 1; ++row) {
        const uchar* kPrevious = input.ptr<const uchar>(row - 1);
        const uchar* kCurrent  = input.ptr<const uchar>(row);
        const uchar* kNext     = input.ptr<const uchar>(row + 1);

        uchar* output = result.ptr<uchar>(row);

        for (int col = 1; col < kChannels * (input.cols - 1); ++col) {
            output[col] = saturate_cast<uchar>(
                (kKernel[0][0] * kPrevious[col - kChannels]) + (kKernel[0][1] * kPrevious[col]) +
                (kKernel[0][2] * kPrevious[col + kChannels]) + (kKernel[1][0] * kCurrent[col - kChannels]) +
                (kKernel[1][1] * kCurrent[col]) + (kKernel[1][2] * kCurrent[col + kChannels]) +
                (kKernel[2][0] * kNext[col - kChannels]) + (kKernel[2][1] * kNext[col]) +
                (kKernel[2][2] * kNext[col + kChannels]));
        }
    }

    // 将边缘用0进行填充
    result.row(0).setTo(Scalar(0));
    result.row(result.rows - 1).setTo(Scalar(0));
    result.col(0).setTo(Scalar(0));
    result.col(result.cols - 1).setTo(Scalar(0));
}
int main(void) {
    auto src = imread(kResourcesDir + "test01.jpg", IMREAD_GRAYSCALE);

    resize(src, src, Size(src.cols / 4, src.rows / 4));

    if (src.empty()) {
        cerr << "Error reading image!" << '\n';
        return EXIT_FAILURE;
    }

    const string kInputTitle  = "Input";
    const string kOutputTitle = "Output";
    namedWindow(kInputTitle, WINDOW_AUTOSIZE);
    namedWindow(kOutputTitle, WINDOW_AUTOSIZE);

    imshow(kInputTitle, src);

    cpp_sdk_test::Timer timer;

    Mat dest0;

    timer.Start();
    Sharpen(src, dest0);
    timer.End();

    cout << "Sharpen: " << timer.AverageTime() << "ms" << '\n';
    imshow(kOutputTitle, dest0);
    waitKey(0);

    Mat kernel(3, 3, CV_8SC1, const_cast<char*>(kKernel[0]));
    Mat dest1;

    timer.Start();
    filter2D(src, dest1, src.depth(), kernel);
    timer.End();

    cout << "filter2D: " << timer.AverageTime() << "ms" << '\n';
    imshow(kOutputTitle, dest1);
    waitKey(0);

    destroyAllWindows();

    return 0;
}
